package com.tly.common.util;

import com.tly.common.config.MinioConfig;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 文件上传
 *
 * @author tly12
 */

@Component
@Slf4j
public class MinioUtil {

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinioConfig minioConfig;

    /**
     * 判断bucket是否存在
     *
     * @param name
     */
    public Boolean existBucket(String name) {
        boolean exist = false;
        try {
            exist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
            if (!exist) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return exist;
    }

    /**
     * 创建存储bucket
     *
     * @param bucketName 存储bucket名称
     * @return Boolean
     */
    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 删除存储bucket
     *
     * @param bucketName 存储bucket名称
     * @return Boolean
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 获取全部bucket
     */
    public List<Map<String, Object>> getAllBuckets() {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        List<Map<String, Object>> list = new ArrayList<>();
        try {
            List<Bucket> buckets = minioClient.listBuckets();
            if (buckets != null && buckets.size() > 0) {
                buckets.forEach(bucket -> {
                    HashMap<String, Object> map = new HashMap<>();
                    map.put("bucketName", bucket.name());
                    LocalDateTime createTime = bucket.creationDate().withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime();
                    map.put("createTime", formatter.format(createTime));
                    list.add(map);
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    /**
     * 初始化minio配置
     *
     * @param :
     * @return: void
     * @date : 2020/8/16 20:56
     */
    @PostConstruct
    public void init() {
        try {
            minioClient = MinioClient.builder()
                    .endpoint(minioConfig.getEndpoint())
                    .credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey())
                    .build();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("初始化minio配置异常: 【{}】", e.fillInStackTrace());
        }
    }

    /**
     * putObject上传文件
     *
     * @param file 文件
     * @return filePath
     */
    public Map<String, String> putObject(MultipartFile file) {
        Boolean existBucket = existBucket(minioConfig.getBucketName());
        Map<String, String> resultMap = new HashMap<>();
        if (existBucket) {
            try {
                //文件流
                InputStream inputStream = file.getInputStream();
                //文件大小
                long size = file.getSize();
                //文件路径
                String filePath = extractUploadFilename(file);
                System.out.println(filePath + "\t文件路径");
                //存储方法 putObject
                minioClient.putObject(PutObjectArgs.builder()
                        .bucket(minioConfig.getBucketName())
                        .object(filePath)
                        .stream(inputStream, size, -1)
                        .contentType(file.getContentType())
                        .build());
                // 返回地址
                filePath = minioConfig.getEndpoint() + "/" + minioConfig.getBucketName() + "/" + filePath;
                resultMap.put("url", filePath);
                inputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return resultMap;
    }

    /**
     * 下载文件
     *
     * @param filePath 文件路径
     */
    public void getObject(HttpServletResponse httpServletResponse, String filePath) {
        try {
            URL url = new URL(filePath);
            String path = url.getPath();
            String replace = path.replace("/" + minioConfig.getBucketName(), "");
            String fileName = URLDecoder.decode(replace, StandardCharsets.UTF_8);
            InputStream inputStream = minioClient.getObject(GetObjectArgs.builder()
                    .bucket(minioConfig.getBucketName())
                    .object(fileName)
                    .build());
            downloadFile(httpServletResponse, inputStream, fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 预览图片
     *
     * @param fileName
     * @return
     */
    public String preview(String fileName) {
        // 查看文件地址
        GetPresignedObjectUrlArgs build = GetPresignedObjectUrlArgs.builder().bucket(minioConfig.getBucketName()).object(fileName).method(Method.GET).build();
        try {
            return minioClient.getPresignedObjectUrl(build);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据文件路径获取文件名称
     *
     * @param filePath 文件路径
     * @return 文件名
     */
    public String getFileName(String filePath) {
        String[] split = StringUtils.split(filePath, "/");
        return split[split.length - 1];
    }

    /**
     * 删除
     *
     * @param fileName
     * @return
     * @throws Exception
     */
    public boolean remove(String fileName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(minioConfig.getBucketName()).object(fileName).build());
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 下载文件
     *
     * @param httpServletResponse httpServletResponse
     * @param inputStream         inputStream
     * @param fileName            文件名
     * @throws IOException IOException
     */
    public void downloadFile(HttpServletResponse httpServletResponse, InputStream inputStream, String fileName) throws IOException {
        //设置响应头信息，告诉前端浏览器下载文件
        httpServletResponse.setContentType("application/octet-stream;charset=UTF-8");
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
        //获取输出流进行写入数据
        OutputStream outputStream = httpServletResponse.getOutputStream();
        // 将输入流复制到输出流
        byte[] buffer = new byte[4096];
        int bytesRead = -1;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
        }
        // 关闭流资源
        inputStream.close();
        outputStream.close();
    }

    /**
     * 编码文件名
     * 日期路径 + UUID
     * 示例：fileName=2022/11/18/统计报表1668758006562.txt
     */
    public static final String extractUploadFilename(MultipartFile file) {
        String fileName = file.getOriginalFilename();
        // 注意，这里需要加上 \\ 将 特殊字符 . 转意 \\. ,否则异常
        String[] fileArray = fileName.split("\\.");
        fileName = datePath() + "/" + fileArray[0] + System.currentTimeMillis() + "." + fileArray[1];
        return fileName;
    }

    /**
     * 日期路径 即年/月/日 如2018/08/08
     */
    public static final String datePath() {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyy/MM/dd");
    }

    /**
     * 获取桶对象中所有文件
     */
    public List<Map<String, Object>> listObjects(String bucketName) {
        boolean flag = existBucket(bucketName);
        List<Map<String, Object>> objectItems = new ArrayList<>();
        if (flag) {
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder().bucket(bucketName).recursive(true).build());
            try {
                for (Result<Item> result : results) {
                    Item item = result.get();
                    HashMap<String, Object> map = new HashMap<>();
                    String filePath = minioConfig.getEndpoint() + "/" + minioConfig.getBucketName() + "/" + item.objectName();
                    map.put("fileName", item.objectName());
                    map.put("url", filePath);
                    map.put("size", item.size());
                    map.put("suffix", filePath.substring(filePath.lastIndexOf(".") + 1));
                    objectItems.add(map);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return objectItems;
    }

    /**
     * 批量删除文件
     * @param bucketName
     * @param list
     * @return
     */
    public Map<String, Object> removeList(String bucketName, List<String> list) {
        HashMap<String, Object> map = new HashMap<>();
        List<String> success = new ArrayList<>();
        List<String> fail = new ArrayList<>();
        if (StringUtils.isBlank(bucketName)) {
            bucketName = minioConfig.getBucketName();
        }
        if (list != null && list.size() > 0) {
            String finalBucketName = bucketName;
            list.forEach(i -> {
                try {
                    minioClient.removeObject(RemoveObjectArgs.builder().bucket(minioConfig.getBucketName()).object(finalBucketName).build());
                    success.add(i);
                } catch (Exception e) {
                    fail.add(i);
                }
            });
        }
        map.put("success", success);
        map.put("fail", fail);
        return map;
    }
}
